
# 导入os模块，不过当前代码中未使用该模块，可考虑移除
import os
# 导入shutil模块，不过当前代码中未使用该模块，可考虑移除
import shutil

# 从qtpy库导入QtCore模块，提供核心的非GUI功能
from qtpy import QtCore
# 从qtpy库导入QtGui模块，提供图形用户界面相关的功能
from qtpy import QtGui
# 从qtpy.QtWidgets模块导入所有组件
from qtpy.QtWidgets import *

# 从src模块导入Wizards，不过当前代码中未使用该导入，可考虑移除
from src import Wizards
# 从src.Wizards模块导入WizardDialogueBase类
from src.Wizards.WizardDialogueBase import WizardDialogueBase
# 从src.Wizards模块导入PkgGen中的所有内容
from src.Wizards.PkgGen import *
# 从src模块导入Packages
from src import Packages


class PackageWizard(WizardDialogueBase):
    """
    PackageWizard类继承自WizardDialogueBase，用于创建一个包生成向导对话框。
    该向导引导用户完成包的创建过程，包括包命名、组件选择和输出目录选择等步骤。
    """

    def __init__(self, parent=None):
        """
        初始化PackageWizard对话框。

        :param parent: 父窗口对象，默认为None
        """
        # 调用父类的构造函数
        super(PackageWizard, self).__init__(parent)

    def addFinalPage(self):
        """
        向向导添加最后一页，提示用户一切准备就绪，点击完成即可生成包。
        并提醒用户生成完成后需要重启编辑器才能看到新内容。
        """
        self.addPageWidget(
            QWidget(),
            # 页面提示信息
            "Everything is ready! Click done to generate the package!"
            + "\n\n**Note**: When job will be done, you will need to restart the editor in order to see your new stuff!"
            + "\nGood luck!",
        )

    def onDone(self):
        """
        当用户点击完成按钮时调用此方法。
        收集用户在向导中输入的信息，调用generatePackage函数生成包，然后关闭对话框。
        """
        # 如果执行到这里，说明所有输入都正确
        # 获取用户输入的包名
        packageName = self.lePkgName.text()
        # 判断是否包含UI节点工厂组件
        includeUINodeFactory = (
            self.cbIncludeUINodeFactory.checkState() == QtCore.Qt.Checked
            and self.cbIncludeUINodeFactory.isEnabled()
        )
        # 判断是否包含UI引脚工厂组件
        IncludeUIPinFactory = (
            self.cbUIPinFactory.checkState() == QtCore.Qt.Checked
            and self.cbUIPinFactory.isEnabled()
        )
        # 判断是否包含引脚输入窗口工厂组件
        IncludePinInputWidgetFactory = (
            self.cbPinInputWidgetFactory.checkState() == QtCore.Qt.Checked
            and self.cbPinInputWidgetFactory.isEnabled()
        )
        # 判断是否包含首选项窗口组件
        IncludePrefsWidget = self.cbPrefsWidget.checkState() == QtCore.Qt.Checked
        # 调用generatePackage函数生成包
        generatePackage(
            packageName,
            self.pbOutPathSelect.text(),
            bIncludeClassNode=self.cbIncludeClassNode.checkState() == QtCore.Qt.Checked,
            bIncludeFooLib=self.cbIncludeFooLib.checkState() == QtCore.Qt.Checked,
            bIncludeUINodeFactory=includeUINodeFactory,
            bIncludePin=self.cbIncludePin.checkState() == QtCore.Qt.Checked,
            bIncludeUIPinFactory=IncludeUIPinFactory,
            bIncludeTool=self.cbIncludeTool.checkState() == QtCore.Qt.Checked,
            bIncludeExporter=self.cbIncludeExporter.checkState() == QtCore.Qt.Checked,
            bIncludePinInputWidgetFactory=IncludePinInputWidgetFactory,
            bIncludePrefsWindget=IncludePrefsWidget,
        )
        # 关闭对话框
        self.accept()

    def populate(self):
        """
        填充向导的各个页面，包括包命名页面、组件选择页面和输出目录选择页面。
        """
        # 第一页：包命名页面
        self.p1 = QWidget()
        self.p1Layout = QHBoxLayout(self.p1)
        # 创建一个行编辑框，初始文本为"DemoPackage"，用于用户输入包名
        self.lePkgName = QLineEdit("DemoPackage")
        # 设置输入验证，只允许输入字母，不允许有空格
        self.lePkgName.setValidator(QtGui.QRegularExpressionValidator(QtCore.QRegularExpression("^[A-Za-z]+$")))

        # 设置输入框文本居中显示，此处存在类 'Qt' 的未解析的特性引用问题
        self.lePkgName.setAlignment(QtCore.Qt.AlignCenter)
        self.p1Layout.addWidget(self.lePkgName)
        # 向向导添加第一页
        self.addPageWidget(self.p1, "Choose a name for your new package!")

        # 第二页：组件选择页面
        self.p2 = QWidget()
        self.p2Layout = QVBoxLayout(self.p2)
        self.goToDocsWidget = QWidget()
        self.goToDocsLayout = QHBoxLayout(self.goToDocsWidget)
        # 创建一个间隔项，此处存在类 'QSizePolicy' 的未解析的特性引用问题
        spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Maximum)
        self.goToDocsLayout.addItem(spacer)
        # 创建一个按钮，点击后可跳转到组件文档页面
        self.bpGotoComponentsDocs = QPushButton("?")
        self.bpGotoComponentsDocs.setToolTip("Go to docs")
        self.bpGotoComponentsDocs.clicked.connect(self.onGotoComponentsDocs)
        self.goToDocsLayout.addWidget(self.bpGotoComponentsDocs)
        self.p2Layout.addWidget(self.goToDocsWidget)
        # 创建一个复选框，用于全选组件
        self.toogleAll = QCheckBox("Toogle All")
        self.toogleAll.stateChanged.connect(self.onToogleAll)
        self.p2Layout.addWidget(self.toogleAll)
        # 创建各个组件的复选框
        self.cbIncludeClassNode = QCheckBox("Class node")
        self.cbIncludeClassNode.stateChanged.connect(self.checkIncludeUINodeFactory)
        self.cbIncludeFooLib = QCheckBox("Function library")
        self.cbIncludePin = QCheckBox("Pin")
        self.cbIncludePin.stateChanged.connect(self.checkUIPinFactories)
        self.cbIncludeTool = QCheckBox("Tool")
        self.cbIncludeExporter = QCheckBox("Exporter")

        self.classNodeLayout = QHBoxLayout()
        self.classNodeLayout.setSpacing(1)
        self.classNodeLayout.setContentsMargins(0, 0, 0, 0)
        # 创建包含UI节点工厂的复选框
        self.cbIncludeUINodeFactory = QCheckBox("Include ui node factory")
        self.cbIncludeUINodeFactory.setEnabled(False)
        self.classNodeLayout.addWidget(self.cbIncludeClassNode)
        self.classNodeLayout.addWidget(self.cbIncludeUINodeFactory)
        self.p2Layout.addLayout(self.classNodeLayout)
        self.p2Layout.addWidget(self.cbIncludeFooLib)

        self.pinLayout = QHBoxLayout()
        self.pinLayout.setSpacing(1)
        self.pinLayout.setContentsMargins(0, 0, 0, 0)
        # 创建包含UI引脚工厂的复选框
        self.cbUIPinFactory = QCheckBox("Include ui pin factory")
        self.cbUIPinFactory.setEnabled(False)
        self.pinLayout.addWidget(self.cbIncludePin)
        self.pinLayout.addWidget(self.cbUIPinFactory)
        self.p2Layout.addLayout(self.pinLayout)

        self.toolLayout = QHBoxLayout()
        self.toolLayout.setSpacing(1)
        self.toolLayout.setContentsMargins(0, 0, 0, 0)
        # 创建包含引脚输入窗口工厂的复选框
        self.cbPinInputWidgetFactory = QCheckBox("Include pin input widget factory")
        self.cbPinInputWidgetFactory.setEnabled(False)
        self.toolLayout.addWidget(self.cbIncludeTool)
        self.toolLayout.addWidget(self.cbPinInputWidgetFactory)
        self.p2Layout.addLayout(self.toolLayout)

        # 创建包含首选项窗口的复选框
        self.cbPrefsWidget = QCheckBox("Prefs widget")

        self.p2Layout.addWidget(self.cbIncludeExporter)
        self.p2Layout.addWidget(self.cbPrefsWidget)

        # 向向导添加第二页，并设置验证函数，确保用户至少选择一个组件
        self.addPageWidget(
            self.p2,
            "What components should be included?",
            "Please select at least one component to include to package!",
            self.isPackaeModuleSelected,
        )

        # 第三页：输出目录选择页面
        self.p3 = QWidget()
        self.p3Layout = QHBoxLayout(self.p3)
        # 创建一个按钮，点击后可选择包的输出目录
        self.pbOutPathSelect = QPushButton("...")
        self.p3Layout.addWidget(self.pbOutPathSelect)
        self.pbOutPathSelect.clicked.connect(self.onSelectPackageDirectory)
        # 向向导添加第三页，并设置页面进入时的回调函数
        self.addPageWidget(
            self.p3,
            "Select output directory for your new package!"
            + "\n\n**Note**: Output directory should be writable.",
            pageEnterCallback=self.onSelectPackageRootEntered,
        )

    def onToogleAll(self, state):
        """
        当全选复选框状态改变时调用此方法。
        根据全选复选框的状态，设置所有组件复选框的状态。

        :param state: 全选复选框的状态
        """
        self.cbIncludeClassNode.setChecked(state)
        self.cbIncludeFooLib.setChecked(state)
        self.cbIncludePin.setChecked(state)
        self.cbIncludeTool.setChecked(state)
        self.cbIncludeExporter.setChecked(state)
        self.cbIncludeUINodeFactory.setChecked(state)
        self.cbUIPinFactory.setChecked(state)
        self.cbPinInputWidgetFactory.setChecked(state)
        self.cbPrefsWidget.setChecked(state)

    def checkUIPinFactories(self, state):
        """
        当引脚复选框状态改变时调用此方法。
        根据引脚复选框的状态，启用或禁用UI引脚工厂和引脚输入窗口工厂的复选框。

        :param state: 引脚复选框的状态
        """
        # 判断引脚复选框是否被选中，此处存在类 'Qt' 的未解析的特性引用问题
        checked = self.cbIncludePin.checkState() == QtCore.Qt.Checked
        self.cbPinInputWidgetFactory.setEnabled(checked)
        self.cbUIPinFactory.setEnabled(checked)

    def checkIncludeUINodeFactory(self, state):
        """
        当类节点复选框状态改变时调用此方法。
        根据类节点复选框的状态，启用或禁用包含UI节点工厂的复选框。
        目前只有类节点被选中时，UI节点工厂才能被创建。

        :param state: 类节点复选框的状态
        """
        # 判断类节点复选框是否被选中，此处存在类 'Qt' 的未解析的特性引用问题
        self.cbIncludeUINodeFactory.setEnabled(
            self.cbIncludeClassNode.checkState() == QtCore.Qt.Checked
        )

    def onGotoComponentsDocs(self):
        """
        当点击跳转到组件文档按钮时调用此方法。
        打印提示信息，表示将打开组件文档页面。
        """
        print("Open components docs page")

    def onSelectPackageRootEntered(self):
        """
        当进入输出目录选择页面时调用此方法。
        将输出目录选择按钮的文本设置为Packages模块的路径。
        """
        self.pbOutPathSelect.setText(Packages.__path__[0])

    def isPackaeModuleSelected(self):
        """
        验证用户是否至少选择了一个组件。
        遍历各个组件复选框的状态，只要有一个被选中则返回True，否则返回False。

        :return: 如果至少选择一个组件返回True，否则返回False
        """
        return any(
            [
                self.cbIncludeClassNode.checkState() == QtCore.Qt.Checked,
                self.cbIncludeFooLib.checkState() == QtCore.Qt.Checked,
                self.cbIncludePin.checkState() == QtCore.Qt.Checked,
                self.cbIncludeTool.checkState() == QtCore.Qt.Checked,
                self.cbIncludeExporter.checkState() == QtCore.Qt.Checked,
            ]
        )

    def onSelectPackageDirectory(self, *args):
        """
        当点击输出目录选择按钮时调用此方法。
        弹出目录选择对话框，让用户选择包的输出目录，并将选择的目录路径显示在按钮上。

        :param args: 可变参数
        """
        # 获取用户输入的包名
        packageName = self.lePkgName.text()
        # 弹出目录选择对话框，此处存在类 'QFileDialog' 的未解析的特性引用问题
        packageRoot = QFileDialog.getExistingDirectory(
            self,
            "Choose folder",
            "Choose folder",
            QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks,
        )
        # 拼接新包的路径
        newPackagePath = os.path.join(packageRoot, packageName)
        # 规范化路径格式
        newPackagePath = os.path.normpath(newPackagePath)
        # 将选择的目录路径显示在按钮上
        self.pbOutPathSelect.setText(packageRoot)

    @staticmethod
    def run():
        """
        静态方法，用于创建并显示PackageWizard对话框。
        """
        # 创建PackageWizard对话框实例
        instance = PackageWizard()
        # 显示对话框并进入模态状态
        instance.exec_()